#include <fermat/dio/iostats.h>
#include <fermat/common/string.h>
#include <fermat/common/scoped_lock.h>

#include <iomanip>
#include <sstream>
#include <string>


namespace fermat {

Stats::Stats()
{
    _last_reset_time = Timestamp();

}

void Stats::write_started(size_t size, Timestamp now) {
#if DIO_STATS_TIMING
    if (now == 0)
        now.update();
#else
    (void)now;
#endif
    {
       ScopedMutex lock(&_write_mutex);

        ++_write_ops;
        _write_volume += size;
#if DIO_STATS_TIMING
        Timespan diff = now - _parallel_write_begin;
        _write_time += _acc_writes * diff;
        _parallel_write_begin = now;
        _parallel_write_time += (_acc_writes++) ? diff : 0;
#endif
    }
#if DIO_STATS_TIMING
    {
        ScopedMutex lock(&_io_mutex);

        Timespan diff = now - _parallel_io_begin;
        _parallel_io_time += _acc_ios++ ? diff : 0;
        _parallel_io_begin = now;
    }
#endif
}

void Stats::write_canceled(size_t size) {
    {
       ScopedMutex lock(&_write_mutex);

        --_write_ops;
        _write_volume -= size;
    }
    write_finished();
}

void Stats::write_finished() {
#if DIO_STATS_TIMING
    Timestamp now;
    {
        ScopedMutex lock(&_write_mutex);

        Timespan diff = now - _parallel_write_begin;
        _write_time += _acc_writes * diff;
        _parallel_write_begin = now;
        _parallel_write_time += (_acc_writes--) ? diff : 0;
    }
    {
        ScopedMutex lock(&_io_mutex);

        Timespan diff = now - _parallel_io_begin;
        _parallel_io_time += (_acc_ios--) ? diff : 0;
        _parallel_io_begin = now;
    }
#endif
}

void Stats::write_cached(size_t size) {
    ScopedMutex lock(&_write_mutex);

    ++_cached_write_ops;
    _cached_write_volume += size;
}

void Stats::read_started(size_t size, Timestamp now) {
#if DIO_STATS_TIMING
    if (now == 0)
        now.update();
#else
    (void)now;
#endif
    {
        ScopedMutex rlock(&_read_mutex);

        ++_read_ops;
        _read_volume += size;
#if DIO_STATS_TIMING
        Timespan diff = now - _parallel_read_begin;
        _read_time += static_cast<double>(_acc_reads) * diff;
        _parallel_read_begin = now;
        _parallel_read_time += (_acc_reads++) ? diff : 0;
 #endif
    }
 #if DIO_STATS_TIMING
    {
        ScopedMutex lock(&_io_mutex);

        Timespan diff = now - _parallel_io_begin;
        _parallel_io_time += (_acc_ios++) ? diff : 0;
        _parallel_io_begin = now;
    }
 #endif
}

void Stats::read_canceled(size_t size) {
    {
        ScopedMutex lock(&_read_mutex);

        --_read_ops;
        _read_volume -= size;
    }
    read_finished();
}

void Stats::read_finished() {
#if DIO_STATS_TIMING
    Timestamp now;
    {
        ScopedMutex lock(&_read_mutex);

        Timestamp diff = now - _parallel_read_begin;
        _read_time += _acc_reads * diff;
        _parallel_read_begin = now;
        _parallel_read_time += (_acc_reads--) ? diff : 0;
    }
    {
        ScopedMutex lock(&_io_mutex);

        Timespan diff = now - _parallel_io_begin;
        _parallel_io_time += (_acc_ios--) ? diff : 0;
        _parallel_io_begin = now;
    }
#endif
}

void Stats::read_cached(size_t size) {
    ScopedMutex lock(&_read_mutex);

    ++_cached_read_ops;
    _cached_read_volume += size;
}

#if !DO_NOT_COUNT_WAIT_TIME
void Stats::wait_started(WaitOp wait_op) {
    Timestamp now;
    {
        ScopedMutex lock(&_wait_mutex);

        Timespan diff = now - _parallel_wait_begin;
        _io_wait_time += _acc_waits * diff;
        _parallel_wait_begin = now;
        _parallel_wait_time += (_acc_waits++) ? diff : 0;

        if (wait_op == WaitOp::WOP_READ) {
            diff = now - _parallel_wait_read_begin;
            _read_wait_time += _acc_wait_read * diff;
            _parallel_wait_read_begin = now;
            _parallel_wait_read_time += (_acc_wait_read++) ? diff : 0;
        }
        else /* if (wait_op == WaitOp::WRITE) */ {
            // wait_any() is only used from write_pool and buffered_writer, so account WAIT_OP_ANY for WaitOp::WRITE, too
            diff = now - _parallel_wait_write_begin;
            _write_wait_time += _acc_wait_write * diff;
            _parallel_wait_write_begin = now;
            _parallel_wait_write_time += (_acc_wait_write++) ? diff : 0;
        }
    }
}

void Stats::wait_finished(WaitOp wait_op) {
    Timestamp now;
    {
         ScopedMutex lock(&_wait_mutex);

        Timespan diff = now - _parallel_wait_begin;
        _io_wait_time += _acc_waits * diff;
        _parallel_wait_begin = now;
        _parallel_wait_time += (_acc_waits--) ? diff : 0;

        if (wait_op == WaitOp::WOP_READ) {
            Timespan diff_read = now - _parallel_wait_read_begin;
            _read_wait_time += _acc_wait_read * diff_read;
            _parallel_wait_read_begin = now;
            _parallel_wait_read_time += (_acc_wait_read--) ? diff_read : 0;
        }
        else /* if (wait_op == WaitOp::WRITE) */ {
            Timespan diff_write = now - _parallel_wait_write_begin;
            _write_wait_time += _acc_wait_write * diff_write;
            _parallel_wait_write_begin = now;
            _parallel_wait_write_time += (_acc_wait_write--) ? diff_write : 0;
        }
    }
}
#endif

/*
std::ostream& operator << (std::ostream& o, const StatsData& s)
{
	(void)s;
	return o;
}
*/
}

